﻿using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace Tama_Chan.UI
{
   public class Widget
   {
      public const float DepthIncrement = 0.001f;

      public string Name { get; set; }
      public Widget Parent { get; set; }
      public SpriteBatch SpriteBatch { get; set; }
      public List<Widget> Children { get; set; }
      public Rectangle Bounds { get; set; }
      public float Depth { get; set; }

      public Texture2D CurrentTexture { get; set; }
      public Texture2D NormalTexture { get; set; }
      public Texture2D MouseoverTexture { get; set; }
      public Texture2D SelectedTexture { get; set; }

      public Color CurrentColor { get; set; }
      public Color NormalColor { get; set; }
      public Color MouseoverColor { get; set; }
      public Color SelectedColor { get; set; }

      private bool isVisible;
      public bool IsContainer;
      public bool IsBlock { get; set; }
      public HorizontalAlignment HorizontalAlignment { get; set; }
      public string ButtonGroup { get; set; }
      public bool IsSelected { get; private set; }

      private int marginTop;
      public int MarginRight { get; set; }
      public int MarginBottom { get; set; }
      private int marginLeft;

      private int paddingTop;
      private int paddingRight;
      private int paddingBottom;
      private int paddingLeft;


      public Widget(SpriteBatch spriteBatch, Rectangle bounds, Texture2D texture)
      {
         /*
          * This should only ever used to produce a top-level container (think 'window'). The window will fit
          * into a Rectangle defined by the app. There could be two or more windows in the same HUD.
          */
 
         SpriteBatch = spriteBatch;
         Children = new List<Widget>();
         Bounds = bounds;
         NormalTexture = texture;
         CurrentTexture = texture;
         NormalColor = Color.White;
         CurrentColor = Color.White;
         IsVisible = true;
         IsContainer = true;
         IsBlock = true;
         HorizontalAlignment = HorizontalAlignment.Left;
      }

       
      public void AddWidget(Widget widget)
      {
         // Only contnainers can have children.
         if(!IsContainer)
         {
            return;
         }

         widget.Parent = this;
         widget.Depth = widget.Parent.Depth - DepthIncrement;
         Rectangle widgetBounds = widget.Bounds;
         Widget immediateSibling = widget.Parent.Children.Any(w => w.IsVisible) ? widget.Parent.Children.Where(w => w.IsVisible).Last() : null;

         if(widget.IsBlock)
         {
            /*
             * Block. Can be aligned center.
             */
 
            if(widget.HorizontalAlignment == HorizontalAlignment.Left)
            {
               // Adjust relative to parent
               widgetBounds.X += (widget.Parent.Bounds.X + widget.Parent.PaddingLeft);

               // Adjust relative to immediate sibling if present, parent if not.
               if(immediateSibling != null)
               {
                  widgetBounds.Y += immediateSibling.Bounds.Bottom;
               }
               else
               {
                  widgetBounds.Y += (widget.Parent.Bounds.Y + widget.Parent.PaddingTop);
               }
            }
            else if(widget.HorizontalAlignment == HorizontalAlignment.Right)
            {
               // todo - not implemented.
            }
            else // center
            {
               // Adjust relative to parent
               widgetBounds.X = (widget.Parent.Bounds.X + ((widget.Parent.Bounds.Width - widget.Bounds.Width) / 2));

               // Adjust for immediate sibling
               if(immediateSibling != null)
               {
                  widgetBounds.Y += immediateSibling.Bounds.Bottom;
               }
               else
               {
                  widgetBounds.Y += (widget.Parent.Bounds.Y + widget.Parent.PaddingTop);
               }
            } 
         }
         else
         {
            /*
             * Inline. No adjustment for alignment.
             */

            if(immediateSibling != null)
            {
               /*
                * Adjust relative to immediate sibling.
                */

               if(immediateSibling.IsBlock)
               {
                  widgetBounds.X += (widget.Parent.Bounds.X + widget.Parent.PaddingLeft);
                  widgetBounds.Y += (immediateSibling.Bounds.Bottom + immediateSibling.MarginBottom);
               }
               else
               {
                  widgetBounds.X += (immediateSibling.Bounds.Right + immediateSibling.MarginRight);
                  widgetBounds.Y += (immediateSibling.Bounds.Top);
               }
            }
            else
            {
               /*
                * Adjust relative to parent.
                */

               widgetBounds.X += (widget.Parent.Bounds.X + widget.Parent.PaddingLeft);
               widgetBounds.Y += (widget.Parent.Bounds.Y + widget.Parent.PaddingTop);
            }
         }

         widget.Bounds = widgetBounds;

         Children.Add(widget);
      }


      public void Draw()
      {
         if(!IsVisible)
         {
            return;
         }

         SpriteBatch.Draw(CurrentTexture, 
                          Bounds, 
                          null,
                          CurrentColor,
                          0f,
                          Vector2.Zero,
                          SpriteEffects.None,
                          Depth);

         if(Children == null)
         {
            return; ;
         }

         foreach (var widget in Children)
         {
            if(widget.IsVisible)
            {
               widget.Draw();
            }
         }
      }


      public bool IsVisible
      {
         get
         {
            return WidgetIsVisible(this);
         }

         set
         {
            isVisible = value;
         }
      }


      public int MarginTop
      {
         get
         {
            return marginTop;
         }

         set
         {
            marginTop = value;
            if (!IsBlock) return;
            Rectangle r = Bounds;
            r.Y += marginTop;
            Bounds = r;
         }
      }


      public int MarginLeft
      {
         get
         {
            return marginLeft;
         }

         set
         {
            marginLeft = value;
            Rectangle r = Bounds;
            r.X += marginLeft;
            Bounds = r;
         }
      }


      public int PaddingTop
      {
         get
         {
            return paddingTop;
         }

         set
         {
            paddingTop = value;
            if (!IsContainer) return;
            Rectangle bounds = Bounds;
            bounds.Height += value;
            Bounds = bounds;
         }
      }


      public int PaddingRight
      {
         get
         {
            return paddingRight;
         }

         set
         {
            paddingRight = value;
            if (!IsContainer) return;
            Rectangle bounds = Bounds;
            bounds.Width += value;
            Bounds = bounds;
         }
      }


      public int PaddingBottom
      {
         get
         {
            return paddingBottom;
         }
         set
         {
            paddingBottom = value;
            if(!IsContainer) return;
            Rectangle bounds = Bounds;
            bounds.Height += value;
            Bounds = bounds;
         }
      }


      public int PaddingLeft
      {
         get
         {
            return paddingLeft;
         }

         set
         {
            paddingLeft = value;
            if(!IsContainer) return;
            Rectangle bounds = Bounds;
            bounds.Width += value;
            Bounds = bounds;
         }
      }


      private static bool WidgetIsVisible(Widget widget)
      {
         /*
          * Don't use the property (IsVisible) but instead
          * the underlying bool or a stackoverflow will occur.
          */

         if(!widget.isVisible)
         {
            return false;
         }

         if(widget.isVisible && widget.Parent == null)
         {
            return true;
         }

         return WidgetIsVisible(widget.Parent);
      }


      public bool Contains(Vector2 v2)
      {
         return IsVisible && Bounds.Contains((int)v2.X, (int)v2.Y);
      }


      public void Select()
      {
         if(ButtonGroup != null)
         {
            // Clicking a selected radiobutton does nothing
            if(IsSelected)
            {
               return;
            }

            var widgets = Parent.Children.Where(x => x.ButtonGroup == ButtonGroup && x != this);
            foreach (var widget in widgets)
            {
               widget.IsSelected = false;
               widget.CurrentTexture = NormalTexture;
            }
         }

         IsSelected = !IsSelected;
         CurrentTexture = IsSelected ? SelectedTexture : NormalTexture;
      }

   }
}
